package com.matrix.storage.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.matrix.api.storage.StorageApi;
import com.matrix.api.storage.dto.FileUploadDTO;
import com.matrix.common.exception.BusinessException;
import com.matrix.storage.config.MinioProperties;
import com.matrix.storage.service.StorageService;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.tika.Tika;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author 有点甜
 * @since 2025/7/25
 */
@Service
@Slf4j
@RequiredArgsConstructor
@DubboService
public class StorageServiceImpl implements StorageService, StorageApi {

    private final MinioClient minioClient;
    private final MinioProperties minioProperties;
    private final Tika tika = new Tika();

    private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList("image/jpeg", "image/png");
    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;

    @PostConstruct
    public void initBuckets() {
        minioProperties.getBucket().values().forEach(bucketName -> {
            try {
                if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                    log.info("Bucket '{}' created successfully.", bucketName);
                }
            } catch (Exception e) {
                throw new RuntimeException("Failed to initialize MinIO buckets", e);
            }
        });
    }

    @Override
    public String uploadInternalFile(FileUploadDTO fileDTO, Long userId, String bucketKey) {
        MultipartFile multipartFile = new MockMultipartFile(
                "file",
                fileDTO.getOriginalFilename(),
                fileDTO.getContentType(),
                fileDTO.getContent()
        );
        return this.uploadFile(multipartFile, userId, bucketKey);
    }

    @Override
    public String uploadFile(MultipartFile file, Long userId, String bucketKey) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("上传文件不能为空");
        }
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new BusinessException("上传文件不能超过5MB");
        }

        byte[] fileBytes;
        try {
            fileBytes = file.getBytes();
        } catch (IOException e) {
            throw new BusinessException("读取上传文件失败");
        }

        String detectedType = tika.detect(fileBytes);
        log.info("上传文件: {}, 原始ContentType: {}, Tika检测到的Type: {}", file.getOriginalFilename(), file.getContentType(), detectedType);

        switch (bucketKey) {
            case "avatar", "qrcode", "review", "license":
                if (!ALLOWED_IMAGE_TYPES.contains(detectedType)) {
                    throw new BusinessException("请上传JPG、PNG格式的图片");
                }
                break;
            default:
                throw new BusinessException("无效的文件上传类型");
        }

        String bucketName = minioProperties.getBucket().get(bucketKey);
        if (!StringUtils.hasText(bucketName)) {
            throw new BusinessException("系统错误：找不到对应的存储桶配置 '" + bucketKey + "'");
        }

        String datePath = DateUtil.format(new Date(), "yyyy/MM/dd");
        String fileExtension = FileUtil.getSuffix(file.getOriginalFilename());
        String objectName = String.format("%s/%d/%s.%s", datePath, userId, UUID.randomUUID(), fileExtension);

        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(detectedType)
                            .build()
            );
            log.info("文件成功上传至 {}/{}", bucketName, objectName);
        } catch (Exception e) {
            log.error("上传文件到 MinIO 失败", e);
            throw new BusinessException("文件上传失败，请稍后再试");
        }

        return String.format("%s/%s/%s", minioProperties.getEndpoint().replaceFirst("/$", ""), bucketName, objectName);
    }
}